﻿// <author>Netmon Cipher Dev Team</author>
// <date>2009-07-16</date>
// <version>1.0</version>
// 
// <summary>
// This contains methods to Decrypt the Encrypted Application Data
// transferred over the SSL layer.
// </summary>

namespace SSLDecryptionExpert
{
    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using AesLib;

    /// <summary>
    /// Application Data Decryption Class
    /// </summary>
    public static class AppDataDecryption
    {
        #region Decrypt SSL Application Data

        /// <summary>
        /// This Method is used to Decrypt the encrypted application data.
        /// </summary>        
        /// <param name="sourceIP">Parameter for source IP address</param>
        /// <param name="cipherSuite">Cipher Suite Information</param>
        /// <returns>Returns True if Decryption is successful.</returns>
        public static bool DecryptSslApplicationData(
                                        string sourceIP,
                                        Variables.CipherSuiteInfo cipherSuite)
        {
            if (Variables.encryptedApplicationData != null)
            {
                Variables.Log.LogMessage("Decrypting Application Data...");

                switch (cipherSuite.DataEncryption)
                {
                    case DataEncryptionMethod.AES128:
                    case DataEncryptionMethod.AES256:

                        //// Decrypting The Application data for AES
                        DecryptAESApplicationData(sourceIP, cipherSuite);

                        break;

                    case DataEncryptionMethod.RC4_128:

                        //// Decrypting The Application data for RC4
                        DecryptRC4ApplicationData(sourceIP);
                        break;

                    case DataEncryptionMethod.DES3_EDE:

                        //// Decrypting The Application data for Triple DES
                        DecryptTripleDESApplicationData(sourceIP);
                        break;

                    case DataEncryptionMethod.NULL:

                        ////Since the data is not encrypted, 
                        ////copying encrypted data directly to decrypted data
                        Variables.decryptedApplicationData = 
                            Variables.encryptedApplicationData;
                        
                        if (sourceIP == Variables.clientIP)
                        {
                            Variables.isSourceClient = true;
                        }
                        else
                        {
                            Variables.isSourceClient = false;
                        }

                        break;
                }

                try
                {
                    LogApplicationDataDetails(sourceIP, cipherSuite);
                }
                catch (NullReferenceException e)
                {
                    Variables.Log.LogMessage("Exception: " + e.Message + e.StackTrace);
                }

                ////Verifying MAC value
                VerifyMacValue(cipherSuite, sourceIP);
            }         

            return true;
        }

        #endregion

        #region Decrypt SSL 2.0 Application Data

        /// <summary>
        /// This Method is used to Decrypt the encrypted application data for SSL 2.0.
        /// </summary>        
        /// <param name="sourceIP">Parameter for source IP address</param>
        /// <param name="cipherSuite">Cipher Suite Information</param>
        /// <returns>Returns True if Decryption is successful.</returns>
        public static bool DecryptSslV2ApplicationData(
                                        string sourceIP,
                                        Variables.CipherSuiteInfo cipherSuite)
        {
            ComputeKeysSSLV2(cipherSuite);
            Variables.decryptedApplicationData = new byte[Variables.encryptedApplicationData.Length];

            if (cipherSuite.DataEncryption == DataEncryptionMethod.RC4_128)
            {
                RC4Key keyRC4;
                byte[] temp;

                if (sourceIP == Variables.clientIP)
                {
                    Variables.isSourceClient = true;
                    keyRC4 = ARC4.PrepareKey(Variables.clientWriteKey);

                    if (Variables.rc4ClientEncryptedData == null)
                    {
                        Variables.rc4ClientEncryptedData = new byte[0];
                    }

                    Utils.AppendArray(
                                ref Variables.rc4ClientEncryptedData,
                                Variables.encryptedApplicationData);

                    ////Decrypts the Encrypted Application Data
                    temp = ARC4.RC4Crypt(
                                Variables.rc4ClientEncryptedData,
                                keyRC4);
                }
                else
                {
                    Variables.isSourceClient = false;
                    keyRC4 = ARC4.PrepareKey(Variables.serverWriteKey);
                    if (Variables.rc4ServerEncryptedData == null)
                    {
                        Variables.rc4ServerEncryptedData = new byte[0];
                    }

                    Utils.AppendArray(
                                ref Variables.rc4ServerEncryptedData,
                                Variables.encryptedApplicationData);

                    ////Decrypts the Encrypted Application Data
                    temp = ARC4.RC4Crypt(Variables.rc4ServerEncryptedData, keyRC4);
                }

                Buffer.BlockCopy(
                                temp,
                                temp.Length - Variables.encryptedApplicationData.Length,
                                Variables.decryptedApplicationData,
                                0,
                                Variables.decryptedApplicationData.Length);
            }
            else if (cipherSuite.DataEncryption == DataEncryptionMethod.DES3_EDE)
            {
                DecryptTripleDESApplicationData(sourceIP);
            }

            try
            {
                LogApplicationDataDetails(sourceIP, cipherSuite);
            }
            catch (NullReferenceException e)
            {
                Variables.Log.LogMessage("Exception: " + e.Message + e.StackTrace);
            }

            bool returnValue = VerifyMacvalueSSlV2(cipherSuite, sourceIP);

            if (!returnValue)
            {
                Variables.Log.LogMessage("Mac value is incorrect");
                Variables.isMacVerified = false;
            }

            return true;
        }

        #endregion

        #region Computing PreMasterKey

        /// <summary>
        /// This Method is used to compute PreMasterKey using the Certificate privateKey,
        /// by decrypting the encrypted Client Excahnge message
        /// </summary>
        /// <param name="certificateFilePath">Server Certificate Path</param>
        /// <param name="certificatePassword">Server Certificate Password</param>
        /// <param name="keyExchangeData">Key Exchange Data</param>
        /// <param name="cipherInfo">Cipher Suite Information</param>
        /// <returns>Returns TRUE if Pre Master Key is computed.</returns>
        public static bool ComputePreMasterKey(
                                    string certificateFilePath,
                                    string certificatePassword,
                                    byte[] keyExchangeData,
                                    Variables.CipherSuiteInfo cipherInfo)
        {
            if (cipherInfo.KeyExchangeAlg == KeyExchangeAlgorithm.RSA)
            {
                bool error = PreMasterKeyForRSA(
                                certificateFilePath,
                                certificatePassword,
                                keyExchangeData);

                return error;
            }

            return false;
        }

        #endregion

        #region ComputeMasterSecretKey

        /// <summary>
        /// This Method is used to compute MasterSecretKey using the Premasterkey
        /// Client and Server Random Numbers as per RFC 2246
        /// </summary>
        /// <param name="cipherInfo">
        /// Cipher Suite Information
        /// </param>
        /// <returns>
        /// Returns TRUE if Master Secret Key is computed.
        /// </returns>
        public static bool ComputeMasterSecretKey(
                                    Variables.CipherSuiteInfo cipherInfo)
        {
            bool computed = false;
            byte[] appendedArray = new byte[0];

            // Appending Client Random Number with Server Random Number.
            Utils.AppendArray(ref appendedArray, Variables.clientRandomNumber);
            Utils.AppendArray(ref appendedArray, Variables.serverRandomNumber);

            Variables.Log.LogMessage(" ");
            Variables.Log.LogMessage("Computing MasterSecret...");

            try
            {
                if (cipherInfo.SslVersion.Major == 3 &&
                    (cipherInfo.SslVersion.Minor == 1 ||
                     cipherInfo.SslVersion.Minor == 2))
                {
                    ////label used to compute MasterSecret should be "master secret" as per RFC 2246
                    string masterSecretLabelStr = "master secret";
                    byte[] masterSecreteLabel = Encoding.ASCII.GetBytes(masterSecretLabelStr.ToCharArray());

                    ////computing the masterSecretkey for TLS 1.0 as per RFC 2246.
                    PRF_RFC2246(
                            Variables.preMasterSecret,
                            masterSecreteLabel,
                            appendedArray,
                            48,
                            out Variables.masterSecretKey);
                }
                else if (cipherInfo.SslVersion.Major == 3 &&
                         cipherInfo.SslVersion.Minor == 0)
                {
                    ////Computing the masterSecretKey for SSL 3.0
                    ComputeMasterSecret_SSL3(
                                        Variables.preMasterSecret,
                                        appendedArray,
                                        48,
                                        out Variables.masterSecretKey);
                }

                Variables.Log.LogMessage(Variables.masterSecretKey, "MasterKey");
                computed = true;
            }
            catch
            {
                Variables.Log.DisplayException(
                    "Error occurred while computing Master Secret.");
                computed = false;
            }

            return computed;
        }

        #endregion

        #region ComputeKeyBlock

        /// <summary>
        /// Computes the Key Block
        /// </summary>
        /// <param name="cipherInfo">
        /// Cipher Suite Information
        /// </param>
        /// <returns>
        /// Returns TRUE if Key Block is computed.
        /// </returns>
        public static bool ComputeKeyBlock(Variables.CipherSuiteInfo cipherInfo)
        {
            byte[] appendedArray = new byte[0];
            //// Appending Server Random Number with Client Random number.            
            Utils.AppendArray(ref appendedArray, Variables.serverRandomNumber);
            Utils.AppendArray(ref appendedArray, Variables.clientRandomNumber);

            if (cipherInfo.SslVersion.Major == 3 &&
                    (cipherInfo.SslVersion.Minor == 1 ||
                     cipherInfo.SslVersion.Minor == 2))
            {
                ////label used to compute keyblock should be "key expansion" as per RFC 2246
                string keyblockLabelStr = "key expansion";
                byte[] keyBlockLabel = Encoding.ASCII.GetBytes(keyblockLabelStr.ToCharArray());

                try
                {
                    ////Get KeyBlock using the Masterkey,
                    ////Client and Server Random numbers for TLS 1.0.
                    PRF_RFC2246(
                            Variables.masterSecretKey,
                            keyBlockLabel,
                            appendedArray,
                            Variables.keyBlock.Length,
                            out Variables.keyBlock);
                }
                catch
                {
                    return false;
                }

                ////Logs KeyBlock data in Debug file
                Variables.Log.LogMessage(
                            Variables.keyBlock,
                            "KeyBlock[" + Variables.keyBlock.Length + "]");
            }
            else if (cipherInfo.SslVersion.Major == 3 &&
                     cipherInfo.SslVersion.Minor == 0)
            {
                try
                {
                    ////Get KeyBlock using the Masterkey,
                    ////Client and Server Random numbers for SSL 3.0
                    ComputeMasterSecret_SSL3(
                                    Variables.masterSecretKey,
                                    appendedArray,
                                    Variables.keyBlock.Length,
                                    out Variables.keyBlock);
                }
                catch
                {
                    Variables.Log.LogMessage("Error in Computing KeyBlock");
                    return false;
                }

                Variables.Log.LogMessage(Variables.keyBlock, "KeyBlock");
            }

            ////The key_block is partitioned for client_write_MAC_secret,
            ////as per rfc 2246
            Variables.Log.LogMessage(" ");
            Variables.Log.LogMessage("Computing Client MacWriteKey...");

            Array.Copy(
                    Variables.keyBlock,
                    0,
                    Variables.clientMacWriteKey,
                    0,
                    Variables.clientMacWriteKey.Length);

            Variables.Log.LogMessage(
                        Variables.clientMacWriteKey,
                        "Client MacWriteKey[" + Variables.clientMacWriteKey.Length + "]");

            ////The key_block is partitioned for server_write_MAC_secret,
            ////as per rfc 2246
            Variables.Log.LogMessage(" ");
            Variables.Log.LogMessage("Computing Server MacWriteKey...");

            Array.Copy(
                    Variables.keyBlock,
                    Variables.clientMacWriteKey.Length,
                    Variables.serverMacWriteKey,
                    0,
                    Variables.serverMacWriteKey.Length);

            Variables.Log.LogMessage(
                        Variables.serverMacWriteKey,
                        "Server MacWriteKey[" + Variables.serverMacWriteKey.Length + "]");

            ////The key_block is partitioned for client_write_key,
            ////as per rfc 2246
            Variables.Log.LogMessage(" ");
            Variables.Log.LogMessage("Computing Client WriteKey...");

            Array.Copy(
                    Variables.keyBlock,
                    Variables.clientMacWriteKey.Length + Variables.serverMacWriteKey.Length,
                    Variables.clientWriteKey,
                    0,
                    Variables.clientWriteKey.Length);

            Variables.Log.LogMessage(
                        Variables.clientWriteKey,
                        "Client WriteKey[" + Variables.clientWriteKey.Length + "]");

            ////The key_block is partitioned 
            ////for server_write_key, as per rfc 2246
            Variables.Log.LogMessage(" ");
            Variables.Log.LogMessage("Computing Server WriteKey...");

            int serverWriteKeyIndex = Variables.clientMacWriteKey.Length
                                        + Variables.serverMacWriteKey.Length
                                        + Variables.clientWriteKey.Length;

            Array.Copy(
                    Variables.keyBlock,
                    serverWriteKeyIndex,
                    Variables.serverWriteKey,
                    0,
                    Variables.serverWriteKey.Length);

            Variables.Log.LogMessage(
                    Variables.serverWriteKey,
                    "Server WriteKey[" + Variables.serverWriteKey.Length + "]");

            ////The key_block is partitioned for client_write_IV,
            ////as per rfc 2246
            Variables.Log.LogMessage(" ");
            Variables.Log.LogMessage("Computing ClientIV...");

            int clientIVIndex = Variables.clientMacWriteKey.Length
                                + Variables.serverMacWriteKey.Length
                                + Variables.clientWriteKey.Length
                                + Variables.serverWriteKey.Length;

            Array.Copy(
                    Variables.keyBlock,
                    clientIVIndex,
                    Variables.clientCipherDataIV,
                    0,
                    Variables.clientCipherDataIV.Length);

            Variables.Log.LogMessage(
                    Variables.clientCipherDataIV,
                    "ClientIV[" + Variables.clientCipherDataIV.Length + "]");

            ////The key_block is partitioned for server_write_IV,
            ////as per rfc 2246
            Variables.Log.LogMessage(" ");
            Variables.Log.LogMessage("Computing ServerIV...");

            int serverIVIndex = Variables.clientMacWriteKey.Length
                                + Variables.serverMacWriteKey.Length
                                + Variables.clientWriteKey.Length
                                + Variables.serverWriteKey.Length
                                + Variables.clientCipherDataIV.Length;

            Array.Copy(
                    Variables.keyBlock,
                    serverIVIndex,
                    Variables.serverCipherDataIV,
                    0,
                    Variables.serverCipherDataIV.Length);

            Variables.Log.LogMessage(
                    Variables.serverCipherDataIV,
                    "ServerIV[" + Variables.serverCipherDataIV.Length + "]");

            return true;
        }

        #endregion

        #region PreMasterKey for RSA

        /// <summary>
        /// Method to calculate premaster key if RSA is the Key exchange Algorithm
        /// </summary>
        /// <param name="certificateFilePath">Server Certificate Path</param>
        /// <param name="certificatePassword">Server Certificate Password</param>
        /// <param name="keyExchangeData">Key Exchange Data</param>
        /// <returns>true if computed correctly</returns>
        public static bool PreMasterKeyForRSA(
                                    string certificateFilePath,
                                    string certificatePassword,
                                    byte[] keyExchangeData)
        {
            // initializing RSA.
            RSACryptoServiceProvider objectRSA;

            if (certificateFilePath.ToLower().EndsWith(".pfx"))
            {
                X509Certificate2 cert;
                try
                {
                    Variables.Log.LogMessage(" ");
                    Variables.Log.LogMessage("Validating Server Certificate...");

                    // Load the certificate into an X509Certificate2 object.
                    cert = new X509Certificate2(certificateFilePath, certificatePassword);

                    // initializing RSA object with cert.PrivateKey.
                    if (cert.PrivateKey is RSACryptoServiceProvider)
                    {
                        objectRSA = (RSACryptoServiceProvider)cert.PrivateKey;
                    }
                    else
                    {
                        Variables.Log.LogMessage("Cert type is not RSACryptoServiceProvider");
                        return false;
                    }
                }
                catch (CryptographicException ce)
                {
                    Variables.Log.LogMessage("CertFailed: " + ce.Message);
                    Variables.passwordMismatch = true;
                    return false;
                }
                
                Variables.Log.LogMessage(" ");
                Variables.Log.LogMessage("Following are the details of the certificate:");
                Variables.Log.LogMessage("Key Exchange Algorithm: "
                                         + cert.PrivateKey.KeyExchangeAlgorithm);
                Variables.Log.LogMessage("Signature algorithm: "
                                         + cert.PrivateKey.SignatureAlgorithm);
                Variables.Log.LogMessage("Key size: "
                                         + cert.PrivateKey.KeySize);

                try
                {
                    Variables.Log.LogMessage(" ");
                    Variables.Log.LogMessage("Computing PreMasterSecret...");

                    ////Decrypt to get preMasterSecret byte array.
                    Variables.preMasterSecret = objectRSA.Decrypt(
                                                    keyExchangeData,
                                                    false);

                    //// Logs Premaster key in debug file
                    Variables.Log.LogMessage(
                                    Variables.preMasterSecret,
                                    "PreMasterKey");
                }
                catch (CryptographicException ce)
                {
                    Variables.Log.LogMessage("Invalid Certificate.");
                    Variables.Log.LogMessage(ce.Message);
                    Variables.incorrectCertificate = true;
                    return false;
                }
                catch(Exception e)
                {
                    Variables.Log.LogMessage("Invalid Certificate with general exception.");
                    Variables.Log.LogMessage(e.Message);
                    Variables.incorrectCertificate = true;
                    return false;
                }

                return true;
            }
            else if (certificateFilePath.ToLower().EndsWith(".pem") || certificateFilePath.ToLower().EndsWith(".key"))
            {
                try
                {
                    string pem = File.ReadAllText(certificateFilePath);
                    int start = pem.IndexOf("-----BEGIN RSA PRIVATE KEY-----");
                    if (start < 0)
                    {
                        Variables.Log.LogMessage("Invalid Server Certificate.");
                        Variables.Log.DisplayException("\nInvalid Server Certificate");
                        return false;
                    }

                    start += 31; //// 31 being the -----BEGIN RSA PRIVATE KEY----- length
                    int end = pem.IndexOf("-----END RSA PRIVATE KEY-----", start);
                    if (end < start)
                    {
                        Variables.Log.LogMessage("Invalid Server Certificate.");
                        Variables.Log.DisplayException("\nInvalid Server Certificate");
                        return false;
                    }

                    ////Variables.log.LogMessage(" ");
                    ////Variables.log.LogMessage("Following are the details of the certificate:");
                    ////Variables.log.LogMessage("Certificate Private Key: " + pem);
                    string base64 = pem.Substring(start, (end - start));

                    byte[] certPrivateKey = Convert.FromBase64String(base64);
                    string privateKeyElements = GetRSAPrivateKeyElements(certPrivateKey);
                    objectRSA = new RSACryptoServiceProvider();
                    objectRSA.FromXmlString(privateKeyElements);

                    Variables.Log.LogMessage(" ");
                    Variables.Log.LogMessage("Computing PreMasterSecret...");

                    ////Decrypt to get preMasterSecret byte array.
                    Variables.preMasterSecret = objectRSA.Decrypt(
                                                    keyExchangeData,
                                                    false);

                    //// Logs Premaster key in debug file
                    Variables.Log.LogMessage(Variables.preMasterSecret, "PreMasterKey");
                }
                catch (Exception ex)
                {
                    Variables.Log.LogMessage("Invalid Certificate.");
                    Variables.Log.DisplayException(ex.Message + "\nInvalid Server Certificate");
                    return false;
                }

                return true;
            }

            return false;
        }

        #endregion

        #region ComputeKeys for SSL 2.0

        /// <summary>
        /// Compute key for ssl v2
        /// </summary>
        /// <param name="cipherSuite">Cipher suite information</param>
        private static void ComputeKeysSSLV2(Variables.CipherSuiteInfo cipherSuite)
        {
            MD5CryptoServiceProvider hmd5 = new MD5CryptoServiceProvider();
            byte[] zeroByte = Encoding.ASCII.GetBytes("0".ToCharArray());
            byte[] oneByte = Encoding.ASCII.GetBytes("1".ToCharArray());
            byte[] twoByte = Encoding.ASCII.GetBytes("2".ToCharArray());

            int preMasterLength = Variables.preMasterSecret.Length;
            int challengeLength = Variables.sslv2ChallengeData.Length;
            int connectionIDLength = Variables.sslv2ConnectionId.Length;

            int length = preMasterLength + challengeLength + connectionIDLength;

            byte[] array1MD5 = new byte[length + zeroByte.Length];
            byte[] array2MD5 = new byte[length + oneByte.Length];
            byte[] array3MD5 = new byte[length + twoByte.Length];

            //// KEY-MATERIAL-0 = MD5[ MASTER-KEY, "0", CHALLENGE, CONNECTION-ID ]
            int index = 0;
            Buffer.BlockCopy(Variables.preMasterSecret, 0, array1MD5, index, preMasterLength);

            index += preMasterLength;
            Buffer.BlockCopy(zeroByte, 0, array1MD5, index, zeroByte.Length);

            index += zeroByte.Length;
            Buffer.BlockCopy(Variables.sslv2ChallengeData, 0, array1MD5, index, challengeLength);

            index += challengeLength;
            Buffer.BlockCopy(Variables.sslv2ConnectionId, 0, array1MD5, index, connectionIDLength);

            //// KEY-MATERIAL-1 = MD5[ MASTER-KEY, "1", CHALLENGE, CONNECTION-ID ]
            index = 0;
            Buffer.BlockCopy(Variables.preMasterSecret, 0, array2MD5, index, preMasterLength);

            index += preMasterLength;
            Buffer.BlockCopy(oneByte, 0, array2MD5, index, oneByte.Length);

            index += oneByte.Length;
            Buffer.BlockCopy(Variables.sslv2ChallengeData, 0, array2MD5, index, challengeLength);

            index += challengeLength;
            Buffer.BlockCopy(Variables.sslv2ConnectionId, 0, array2MD5, index, connectionIDLength);

            //// KEY-MATERIAL-2 = MD5[ MASTER-KEY, "2", CHALLENGE, CONNECTION-ID ]
            index = 0;
            Buffer.BlockCopy(Variables.preMasterSecret, 0, array3MD5, index, preMasterLength);

            index += preMasterLength;
            Buffer.BlockCopy(twoByte, 0, array3MD5, index, twoByte.Length);

            index += twoByte.Length;
            Buffer.BlockCopy(Variables.sslv2ChallengeData, 0, array3MD5, index, challengeLength);

            index += challengeLength;
            Buffer.BlockCopy(Variables.sslv2ConnectionId, 0, array3MD5, index, connectionIDLength);

            if (cipherSuite.DataEncryption == DataEncryptionMethod.RC4_128)
            {
                Variables.serverWriteKey = hmd5.ComputeHash(array1MD5);
                Variables.clientWriteKey = hmd5.ComputeHash(array2MD5);
            }
            else
            {
                byte[] key1 = hmd5.ComputeHash(array1MD5);
                byte[] key2 = hmd5.ComputeHash(array2MD5);
                byte[] key3 = hmd5.ComputeHash(array3MD5);
                Variables.serverWriteKey = new byte[24];
                Variables.clientWriteKey = new byte[24];

                Buffer.BlockCopy(key1, 0, Variables.serverWriteKey, 0, key1.Length);
                Buffer.BlockCopy(key2, 0, Variables.serverWriteKey, key1.Length, 8);
                Buffer.BlockCopy(key2, 8, Variables.clientWriteKey, 0, 8);
                Buffer.BlockCopy(key3, 0, Variables.clientWriteKey, 8, key3.Length);
            }
        }
        
        #endregion

        #region Decrypt AES Application Data

        /// <summary>
        /// This method decrypts the application data which is 
        /// encrypted using AES algorithm
        /// </summary>
        /// <param name="sourceIP">
        /// Parameter for source IP address
        /// </param>
        /// <param name="cipherSuite">
        /// Cipher Suite Information
        /// </param>
        private static void DecryptAESApplicationData(
                                        string sourceIP,
                                        Variables.CipherSuiteInfo cipherSuite)
        {
            AesClass aesblk;
            AesClass.KeySize keySizeAES;

            byte[] tempArray = new byte[16];
            byte[] decryptedArray = new byte[16];
            byte[] decryptionIV = new byte[Variables.encryptedApplicationData.Length];

            if (cipherSuite.DataEncryption == DataEncryptionMethod.AES128)
            {
                keySizeAES = AesClass.KeySize.Bits128;
            }
            else
            {
                keySizeAES = AesClass.KeySize.Bits256;
            }

            if (sourceIP == Variables.clientIP)
            {
                Variables.isSourceClient = true;
                aesblk = new AesClass(keySizeAES, Variables.clientWriteKey);

                Buffer.BlockCopy(
                            Variables.clientCipherDataIV,
                            0,
                            decryptionIV,
                            0,
                            Variables.clientCipherDataIV.Length);

                Buffer.BlockCopy(
                            Variables.encryptedApplicationData,
                            0,
                            decryptionIV,
                            Variables.clientCipherDataIV.Length,
                            decryptionIV.Length - Variables.clientCipherDataIV.Length);
            }
            else
            {
                Variables.isSourceClient = false;
                aesblk = new AesClass(keySizeAES, Variables.serverWriteKey);

                Buffer.BlockCopy(
                            Variables.serverCipherDataIV,
                             0,
                             decryptionIV,
                             0,
                             Variables.serverCipherDataIV.Length);

                Buffer.BlockCopy(
                            Variables.encryptedApplicationData,
                             0,
                             decryptionIV,
                             Variables.serverCipherDataIV.Length,
                             decryptionIV.Length - Variables.serverCipherDataIV.Length);
            }

            for (int j = 0; j < Variables.encryptedApplicationData.Length / 16; j++)
            {
                Array.Copy(Variables.encryptedApplicationData, j * 16, tempArray, 0, 16);
                aesblk.InvCipher(tempArray, decryptedArray);

                for (int i = j * 16, k = 0; k < 16; k++, i++)
                {
                    Variables.decryptedApplicationData[i]
                        = (byte)(decryptedArray[k] ^ decryptionIV[i]);
                }
            }

            if (sourceIP == Variables.clientIP)
            {
                Buffer.BlockCopy(
                             Variables.encryptedApplicationData,
                             Variables.encryptedApplicationData.Length - 16,
                             Variables.clientCipherDataIV,
                             0,
                             Variables.clientCipherDataIV.Length);
            }
            else
            {
                Buffer.BlockCopy(
                             Variables.encryptedApplicationData,
                             Variables.encryptedApplicationData.Length - 16,
                             Variables.serverCipherDataIV,
                             0,
                             Variables.serverCipherDataIV.Length);
            }
        }

        #endregion

        #region Decrypt RC4 Application Data

        /// <summary>
        /// This method decrypts the application data 
        /// which is encrypted using RC4 algorithm
        /// </summary>
        /// <param name="sourceIP">Parameter for source IP address</param>
        private static void DecryptRC4ApplicationData(string sourceIP)
        {
            RC4Key keyRC4;
            byte[] temp;

            Variables.decryptedApplicationData =
                new byte[Variables.encryptedApplicationData.Length];

            if (sourceIP == Variables.clientIP)
            {
                Variables.isSourceClient = true;
                keyRC4 = ARC4.PrepareKey(Variables.clientWriteKey);

                if (Variables.rc4ClientEncryptedData == null)
                {
                    Variables.rc4ClientEncryptedData = new byte[0];
                }

                Utils.AppendArray(
                            ref Variables.rc4ClientEncryptedData,
                            Variables.encryptedApplicationData);

                ////Decrypts the Encrypted Application Data
                temp = ARC4.RC4Crypt(
                            Variables.rc4ClientEncryptedData,
                            keyRC4);
            }
            else
            {
                Variables.isSourceClient = false;
                keyRC4 = ARC4.PrepareKey(Variables.serverWriteKey);
                if (Variables.rc4ServerEncryptedData == null)
                {
                    Variables.rc4ServerEncryptedData = new byte[0];
                }

                Utils.AppendArray(
                            ref Variables.rc4ServerEncryptedData,
                            Variables.encryptedApplicationData);

                ////Decrypts the Encrypted Application Data
                temp = ARC4.RC4Crypt(Variables.rc4ServerEncryptedData, keyRC4);
            }

            Buffer.BlockCopy(
                            temp,
                            temp.Length - Variables.encryptedApplicationData.Length,
                            Variables.decryptedApplicationData,
                            0,
                            Variables.decryptedApplicationData.Length);
        }

        #endregion

        #region Decrypt Triple DES Application Data

        /// <summary>
        /// Decrypts the data which is encrypted 
        /// using Triple DES algorithm 
        /// </summary>
        /// <param name="sourceIP">Parameter for source IP address</param>
        private static void DecryptTripleDESApplicationData(string sourceIP)
        {
            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
           
            if (sourceIP == Variables.clientIP)
            {
                Variables.isSourceClient = true;
                tdes.Key = Variables.clientWriteKey;
                tdes.IV = Variables.clientCipherDataIV;
            }
            else
            {
                Variables.isSourceClient = false;
                tdes.Key = Variables.serverWriteKey;
                tdes.IV = Variables.serverCipherDataIV;
            }

            byte[] encryptedData = new byte[Variables.encryptedApplicationData.Length + 8];

            Array.Copy(
                Variables.encryptedApplicationData,
                0,
                encryptedData,
                0,
                Variables.encryptedApplicationData.Length);

            ICryptoTransform decryptor = tdes.CreateDecryptor(tdes.Key, tdes.IV);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write);
            cs.Write(encryptedData, 0, encryptedData.Length);
            ms.Position = 0;
            Variables.decryptedApplicationData = ms.ToArray();

            ms.Close();

            if (sourceIP == Variables.clientIP)
            {
                Buffer.BlockCopy(
                             Variables.encryptedApplicationData,
                             Variables.encryptedApplicationData.Length - Variables.clientCipherDataIV.Length,
                             Variables.clientCipherDataIV,
                             0,
                             Variables.clientCipherDataIV.Length);
            }
            else
            {
                Buffer.BlockCopy(
                             Variables.encryptedApplicationData,
                             Variables.encryptedApplicationData.Length - Variables.serverCipherDataIV.Length,
                             Variables.serverCipherDataIV,
                             0,
                             Variables.serverCipherDataIV.Length);
            }            
        }

        #endregion

        #region Computing pseudo-random function

        /// <summary>
        /// This method is used to compute pseudo-random function as per RFC 2246.
        /// </summary>
        /// <param name="secret">Secrect key</param>
        /// <param name="label">Label Field Value</param>
        /// <param name="seed">Seed Field Value</param>
        /// <param name="requriedlength">Length required for the output buffer</param>
        /// <param name="finalKey">Output Key</param>
        private static void PRF_RFC2246(
                                byte[] secret,
                                byte[] label,
                                byte[] seed,
                                int requriedlength,
                                out byte[] finalKey)
        {
            byte[] hashSeed = new byte[label.Length + seed.Length];
            finalKey = new byte[requriedlength];

            if (secret == null)
            {
                return;
            }

            if (label == null)
            {
                return;
            }

            if (seed == null)
            {
                return;
            }

            byte[] finalKeyFromSha1, finalKeyFromMD5;
            byte[] s1 = new byte[secret.Length / 2];
            byte[] s2 = new byte[secret.Length / 2];
            Array.Copy(secret, 0, s1, 0, s1.Length);
            Array.Copy(secret, s1.Length, s2, 0, s2.Length);
            Array.Copy(label, 0, hashSeed, 0, label.Length);
            Array.Copy(seed, 0, hashSeed, label.Length, seed.Length);

            ////PRF is created by splitting the secret into two halves and
            ////using one half to generate data with P_MD5 and the other half to
            ////generate data with P_SHA-1
            P_SHA1(s2, hashSeed, requriedlength, out finalKeyFromSha1);
            P_MD5(s1, hashSeed, requriedlength, out finalKeyFromMD5);

            for (int i = 0; i < requriedlength; i++)
            {
                finalKey[i] = (byte)(finalKeyFromMD5[i] ^ finalKeyFromSha1[i]);
            }
        }

        #endregion

        #region MasterKey for SSL 3.0

        /// <summary>
        /// Computes Matster Key for SSL3.0 as per ssl 3.0 v2-draft.
        /// </summary>
        /// <param name="preMasterSecret">Pre Master Key</param>
        /// <param name="randomNumber">Client + Server random number</param>
        /// <param name="masterKeySize">Size of the Master Key</param>
        /// <param name="masterKey">Master Secret key</param>
        private static void ComputeMasterSecret_SSL3(
                                byte[] preMasterSecret,
                                byte[] randomNumber,
                                int masterKeySize,
                                out byte[] masterKey)
        {
            masterKey = new byte[masterKeySize];
            int offset = masterKeySize / 16;
            if (preMasterSecret == null)
            {
                return;
            }

            if (randomNumber == null)
            {
                return;
            }

            string[] strSecret = new string[] { "A", "BB", "CCC", "DDDD", "EEEEE", "FFFFFF", "GGGGGGG", "HHHHHHHH" };

            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

            byte[] preMasterPlusCSRandom = new byte[preMasterSecret.Length + randomNumber.Length];

            Array.Copy(preMasterSecret, 0, preMasterPlusCSRandom, 0, preMasterSecret.Length);
            Array.Copy(randomNumber, 0, preMasterPlusCSRandom, preMasterSecret.Length, randomNumber.Length);

            int rem = masterKeySize % 16;
            if (rem != 0)
            {
                offset++;
            }

            for (int index = 0; index < offset; index++)
            {
                byte[] arrSHA = new byte[strSecret[index].Length + preMasterPlusCSRandom.Length];
                byte[] arrSecret = Encoding.ASCII.GetBytes(strSecret[index].ToCharArray());

                Array.Copy(arrSecret, 0, arrSHA, 0, arrSecret.Length);
                Array.Copy(preMasterPlusCSRandom, 0, arrSHA, arrSecret.Length, preMasterPlusCSRandom.Length);

                byte[] shaHash = sha1.ComputeHash(arrSHA);

                byte[] arrMD5 = new byte[preMasterSecret.Length + shaHash.Length];

                Array.Copy(preMasterSecret, 0, arrMD5, 0, preMasterSecret.Length);
                Array.Copy(shaHash, 0, arrMD5, preMasterSecret.Length, shaHash.Length);

                byte[] hashMD5 = md5.ComputeHash(arrMD5);
                if (masterKeySize - (index * 16) < 16)
                {
                    Array.Copy(hashMD5, 0, masterKey, index * 16, masterKeySize - (index * 16));
                }
                else
                {
                    Array.Copy(hashMD5, 0, masterKey, index * 16, hashMD5.Length);
                }
            }
        }

        #endregion

        #region Generate data uisng SHA1

        /// <summary>
        /// This Method is used to generate data with SHA1.
        /// </summary>
        /// <param name="secret">Secret Key</param>
        /// <param name="seed">Seed Field Value</param>
        /// <param name="requiredlength">length Field Value</param>
        /// <param name="finalKey">output data</param>
        private static void P_SHA1(
                           byte[] secret,
                           byte[] seed,
                           int requiredlength,
                           out byte[] finalKey)
        {
            HMACSHA1 hmac = new HMACSHA1(secret);
            byte[] hash, hashA, tempSeed = seed;
            int seedStartPos = 0, finalKeyLength = 0;
            finalKey = new byte[0];
            hashA = hmac.ComputeHash(tempSeed);

            do
            {
                seedStartPos = hashA.Length;
                Array.Resize<byte>(ref tempSeed, hashA.Length + seed.Length);
                Array.Copy(hashA, 0, tempSeed, 0, hashA.Length);
                Array.Copy(seed, 0, tempSeed, seedStartPos, seed.Length);
                hash = hmac.ComputeHash(tempSeed);
                hashA = hmac.ComputeHash(hashA);
                finalKeyLength = finalKey.Length;
                Array.Resize<byte>(ref finalKey, finalKeyLength + hash.Length);
                Array.Copy(hash, 0, finalKey, finalKeyLength, hash.Length);
            }
            while (finalKey.Length < requiredlength);

            if (requiredlength < finalKey.Length)
            {
                Array.Resize<byte>(ref finalKey, requiredlength);
            }
        }

        #endregion

        #region Genereate data using MD5

        /// <summary>
        /// This method is used to generate data with MD5.
        /// </summary>
        /// <param name="secret">Secret Key</param>
        /// <param name="seed">seed Field Value</param>
        /// <param name="requiredlength">length Field Value</param>
        /// <param name="finalKey">output data</param>
        private static void P_MD5(
                        byte[] secret,
                        byte[] seed,
                        int requiredlength,
                        out byte[] finalKey)
        {
            HMACMD5 hmac = new HMACMD5(secret);
            byte[] hash, hashA, tempSeed = seed;
            int seedStartPos = 0, finalKeyLength = 0;
            finalKey = new byte[0];
            hashA = hmac.ComputeHash(tempSeed);

            do
            {
                seedStartPos = hashA.Length;
                Array.Resize<byte>(ref tempSeed, hashA.Length + seed.Length);
                Array.Copy(hashA, 0, tempSeed, 0, hashA.Length);
                Array.Copy(seed, 0, tempSeed, seedStartPos, seed.Length);

                hash = hmac.ComputeHash(tempSeed);
                hashA = hmac.ComputeHash(hashA);

                finalKeyLength = finalKey.Length;
                Array.Resize<byte>(ref finalKey, finalKeyLength + hash.Length);
                Array.Copy(hash, 0, finalKey, finalKeyLength, hash.Length);
            }
            while (finalKey.Length < requiredlength);

            if (requiredlength < finalKey.Length)
            {
                Array.Resize<byte>(ref finalKey, requiredlength);
            }
        }

        #endregion        

        #region Get RSA Key Elements

        /// <summary>
        /// Computes all the elements of private key format 
        /// and return as a string
        /// </summary>
        /// <param name="certPrivateKey">Certificate private key</param>
        /// <returns>return all the elements for Private key</returns>
        private static string GetRSAPrivateKeyElements(byte[] certPrivateKey)
        {
            byte readByte;
            ushort readTwoBytes;
            int elementLength;
            string privateKey;

            if (certPrivateKey == null)
            {
                return string.Empty;
            }

            IntPtr ptrToRSAPrivateKey = Marshal.AllocHGlobal(certPrivateKey.Length);
            ptrToRSAPrivateKey = Marshal.UnsafeAddrOfPinnedArrayElement(certPrivateKey, 0);

            ////Read FirstTwo Bytes
            readTwoBytes = (ushort)Marshal.ReadInt16(ptrToRSAPrivateKey);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(ushort));
            if (readTwoBytes == 0x8130)
            {
                ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));
            }
            else
            {
                ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(ushort));
            }

            ////read version of two bytes
            readTwoBytes = (ushort)Marshal.ReadInt16(ptrToRSAPrivateKey);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(short));

            ////Read reserved field
            readByte = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));

            ////Compute value for Modulus
            ////Compute Length for Modulus
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] modulus = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, modulus, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            ////E, D, P, Q, DP, DQ, IQ;
            ////Compute value for E element
            ////Compute Length for E
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] elementE = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, elementE, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            ////Compute value for D element
            ////Compute Length for D
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] elementD = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, elementD, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            ////Compute value for P element
            ////Compute Length for P
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] elementP = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, elementP, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            ////Compute value for Q element
            ////Compute Length for Q
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] elementQ = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, elementQ, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            ////Compute value for DP element
            ////Compute Length for DP
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] elementDP = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, elementDP, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            ////Compute value for DQ element
            ////Compute Length for DQ
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] elementDQ = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, elementDQ, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            ////Compute value for IQ element
            ////Compute Length for IQ
            elementLength = ComputeElementLength(ref ptrToRSAPrivateKey);
            byte[] elementIQ = new byte[elementLength];
            Marshal.Copy(ptrToRSAPrivateKey, elementIQ, 0, elementLength);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + elementLength);

            RSACryptoServiceProvider rsa = ComputeRSAObject(
                                                modulus,
                                                elementE,
                                                elementD,
                                                elementP,
                                                elementQ,
                                                elementDP,
                                                elementDQ,
                                                elementIQ);

            privateKey = rsa.ToXmlString(true);

            return privateKey;
        }

        /// <summary>
        /// Computes RSA Object from the Private key elemenets
        /// </summary>
        /// <param name="modulus">Modulus for RSA</param>
        /// <param name="elementE">Element E for RSA</param>
        /// <param name="elementD">Element D for RSA</param>
        /// <param name="elementP">Element P for RSA</param>
        /// <param name="elementQ">Element Q for RSA</param>
        /// <param name="elementDP">Element DP for RSA</param>
        /// <param name="elementDQ">Element DQ for RSA</param>
        /// <param name="elementIQ">Element IQ for RSA</param>
        /// <returns>Return RSACryptoservice provider object</returns>
        private static RSACryptoServiceProvider ComputeRSAObject(
                                                    byte[] modulus,
                                                    byte[] elementE,
                                                    byte[] elementD,
                                                    byte[] elementP,
                                                    byte[] elementQ,
                                                    byte[] elementDP,
                                                    byte[] elementDQ,
                                                    byte[] elementIQ)
        {
            RSACryptoServiceProvider cryptoProviderRSA = new RSACryptoServiceProvider();
            RSAParameters rsaParams = new RSAParameters();
            rsaParams.Modulus = modulus;
            rsaParams.Exponent = elementE;
            rsaParams.D = elementD;
            rsaParams.P = elementP;
            rsaParams.Q = elementQ;
            rsaParams.DP = elementDP;
            rsaParams.DQ = elementDQ;
            rsaParams.InverseQ = elementIQ;
            cryptoProviderRSA.ImportParameters(rsaParams);
            return cryptoProviderRSA;
        }

        /// <summary>
        /// Computes the length
        /// </summary>
        /// <param name="ptrToRSAPrivateKey">Handle to Privatekey</param>
        /// <returns>Length Field Value</returns>
        private static int ComputeElementLength(ref IntPtr ptrToRSAPrivateKey)
        {
            byte readByte;
            int length = 0;
            readByte = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));
            ////check if the first byte is 0X02
            if (readByte != 0x02)
            {
                return 0;
            }

            readByte = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);
            ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));

            ////check if the Next byte is 0X82
            if (readByte == 0x82)
            {
                ////Compute High value and low value which will be used to compute the Length
                byte highValue = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);
                ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));

                byte lowValue = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);
                ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));
                byte[] buffer = { lowValue, highValue, 0x00, 0x00 };
                length = BitConverter.ToInt32(buffer, 0);
            }
            else if (readByte == 0x81)
            {
                length = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);
                ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));
            }
            else
            {
                length = readByte;
            }

            readByte = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);

            while (0x00 == readByte)
            {
                ////remove high order zeros in data
                length--;
                ptrToRSAPrivateKey = (IntPtr)((int)ptrToRSAPrivateKey + sizeof(byte));
                readByte = (byte)Marshal.ReadByte(ptrToRSAPrivateKey);
            }

            return length;
        }

        #endregion

        #region Log Application Data Details

        /// <summary>
        /// This method logs current Application data (Plain and Cipher)
        /// and IV for next Application data
        /// </summary>
        /// <param name="sourceIP">Parameter for source IP address</param>
        /// <param name="cipherSuite">Cipher Suite Information</param>
        private static void LogApplicationDataDetails(
                                string sourceIP, 
                                Variables.CipherSuiteInfo cipherSuite)
        {           
            ////Logs Client MacWriteKey in Debug file
            Variables.Log.LogMessage(
                    Variables.encryptedApplicationData,
                    "Cipher Text[" + Variables.encryptedApplicationData.Length + "]");

            ////Logs Client MacWriteKey in Debug file
            Variables.Log.LogMessage(
                    Variables.decryptedApplicationData,
                    "Plain Text[" + Variables.decryptedApplicationData.Length + "]");

            if (cipherSuite.DataEncryption != DataEncryptionMethod.RC4_128)
            {
                if (sourceIP == Variables.clientIP)
                {
                    Variables.Log.LogMessage("Computing ClientIV for next application data");
                    Variables.Log.LogMessage(
                        Variables.clientCipherDataIV,
                        "ClientIV[" + Variables.clientCipherDataIV.Length + "]");
                }
                else
                {
                    Variables.Log.LogMessage("Computing ServerIV for next application data");
                    Variables.Log.LogMessage(
                        Variables.serverCipherDataIV,
                        "ServerIV[" + Variables.serverCipherDataIV.Length + "]");
                }
            }            
        }

        #endregion

        #region Mac verfication

        /// <summary>
        /// This method verifies the MAC value
        /// </summary>        
        /// <param name="cipherSuite">Cipher Suite Information</param>
        /// <param name="sourceIP">Parameter for source IP address</param>
        private static void VerifyMacValue(
                                Variables.CipherSuiteInfo cipherSuite,
                                string sourceIP)
        {
            bool verifyMac = false;

            if (cipherSuite.SslVersion.Major == 3 &&
                    (cipherSuite.SslVersion.Minor == 1 ||
                     cipherSuite.SslVersion.Minor == 2))
            {
                verifyMac = VerifyMacValueTLS(cipherSuite, sourceIP);
            }
            else if ((cipherSuite.SslVersion.Major == 3) &&
                         (cipherSuite.SslVersion.Minor == 0))
            {
                verifyMac = VerifyMacvalueSSl(cipherSuite, sourceIP);
            }

            if (!verifyMac)
            {
                Variables.Log.LogMessage("Mac value is incorrect");
                Variables.isMacVerified = false;
            }
        }
        
        /// <summary>
        /// Method to verify Mac Value for TLS
        /// as per RFC 2246.
        /// </summary>
        /// <param name="cipherSuite">Cipher Suite Information</param>
        /// <param name="sourceIP">Source Ip Address</param>
        /// <returns>true if mac value is Ok</returns>
        private static bool VerifyMacValueTLS(
                                    Variables.CipherSuiteInfo cipherSuite,
                                    string sourceIP)
        {
            HMACSHA1 hsha1 = new HMACSHA1();
            HMACMD5 hmd5 = new HMACMD5();
            byte[] actualMacValue = new byte[0];

            if (cipherSuite.MacType == MAC.SHA1)
            {
                actualMacValue = new byte[20];
            }
            else if (cipherSuite.MacType == MAC.MD5)
            {
                actualMacValue = new byte[16];
            }

            byte[] datalength = new byte[2];
            byte[] sequenceNumber = new byte[8];
            byte[] version = new byte[2];
            byte[] contentType = new byte[1];
            int paddingValue = 0;

            version[0] = (byte)cipherSuite.SslVersion.Major;
            version[1] = (byte)cipherSuite.SslVersion.Minor;
            contentType[0] = Variables.contentType;

            if (sourceIP == Variables.clientIP)
            {
                hsha1.Key = Variables.clientMacWriteKey;
                hmd5.Key = Variables.clientMacWriteKey;
                sequenceNumber = BitConverter.GetBytes(
                                    Variables.clientSequenceNumber);
            }
            else
            {
                hsha1.Key = Variables.serverMacWriteKey;
                hmd5.Key = Variables.serverMacWriteKey;
                sequenceNumber = BitConverter.GetBytes(
                                    Variables.serverSequenceNumber);
            }

            Array.Reverse(sequenceNumber);

            if (cipherSuite.CipherMode == CipherMode.CBC)
            {
                paddingValue =
                    (int)Variables.decryptedApplicationData[Variables.decryptedApplicationData.Length - 1];

                if (paddingValue > 0x0F)
                {
                    Variables.Log.LogMessage("VerifyMacValueTLS.a - MAC Verification failed");
                    paddingValue = -1;
                    //return false;
                }

                for (int i = Variables.decryptedApplicationData.Length - (paddingValue - 1);
                         i < Variables.decryptedApplicationData.Length; i++)
                {
                    if (Variables.decryptedApplicationData[i] != paddingValue)
                    {
                        Variables.Log.LogMessage("VerifyMacValueTLS.b - MAC Verification failed");
                        return false;
                    }
                }

                paddingValue++;
            }

            ////Length for the data from Decrypted data for which mac to be computed
            short len;
            byte[] dataforMac;
            if ((version[0] == 3) && (version[1] == 2))
            {
                len = Convert.ToInt16(
                                    Variables.decryptedApplicationData.Length
                                    - (int)cipherSuite.BlockSize
                                    - actualMacValue.Length
                                    - paddingValue);

                datalength = BitConverter.GetBytes(len);
                Array.Reverse(datalength);

                //// Size of data for mac
                //// decryptedapplication data length
                //// - SHA MAC size(20) - Padding - IV Length
                //// + sequence Number(8) + Contenttype(1) + Version(2) + Length(2)
                dataforMac =
                    new byte[Variables.decryptedApplicationData.Length
                             - (int)cipherSuite.BlockSize
                             - actualMacValue.Length
                             - paddingValue
                             + sequenceNumber.Length
                             + contentType.Length
                             + version.Length
                             + datalength.Length];
            }
            else
            {
                len = Convert.ToInt16(
                                     Variables.decryptedApplicationData.Length
                                     - actualMacValue.Length
                                     - paddingValue);

                datalength = BitConverter.GetBytes(len);
                Array.Reverse(datalength);

                //// Size of data for mac
                //// decryptedapplication data length
                //// - SHA MAC size(20) - Padding
                //// + sequence Number(8) + Contenttype(1) + Version(2) + Length(2)
                dataforMac =
                    new byte[Variables.decryptedApplicationData.Length
                             - actualMacValue.Length
                             - paddingValue
                             + sequenceNumber.Length
                             + contentType.Length
                             + version.Length
                             + datalength.Length];
            }                        
                        
            Buffer.BlockCopy(
                        sequenceNumber,
                        0,
                        dataforMac,
                        0,
                        sequenceNumber.Length);
            Buffer.BlockCopy(
                        contentType,
                        0,
                        dataforMac,
                        sequenceNumber.Length,
                        contentType.Length);
            Buffer.BlockCopy(
                        version,
                        0,
                        dataforMac,
                        sequenceNumber.Length + contentType.Length,
                        version.Length);
            Buffer.BlockCopy(
                        datalength,
                        0,
                        dataforMac,
                        sequenceNumber.Length + contentType.Length + version.Length,
                        datalength.Length);
                        
            if ((version[0] == 3) && (version[1] == 2))
            {
                Buffer.BlockCopy(
                            Variables.decryptedApplicationData,
                            (int)cipherSuite.BlockSize,
                            dataforMac,
                            sequenceNumber.Length + contentType.Length + version.Length + datalength.Length,
                            len);

                ////Mac value from decryptedApplicationData
                Buffer.BlockCopy(
                            Variables.decryptedApplicationData,
                            (int)cipherSuite.BlockSize + len,
                            actualMacValue,
                            0,
                            actualMacValue.Length);
            }
            else
            {
                Buffer.BlockCopy(
                            Variables.decryptedApplicationData,
                            0,
                            dataforMac,
                            sequenceNumber.Length + contentType.Length + version.Length + datalength.Length,
                            len);

                ////Mac value from decryptedApplicationData
                Buffer.BlockCopy(
                            Variables.decryptedApplicationData,
                            len,
                            actualMacValue,
                            0,
                            actualMacValue.Length);
            }

            Variables.Log.LogMessage(dataforMac, "Data for MAC Evaluation");


            byte[] macValue = new byte[0];

            if (cipherSuite.MacType == MAC.SHA1)
            {
                macValue = hsha1.ComputeHash(dataforMac);
            }
            else if (cipherSuite.MacType == MAC.MD5)
            {
                macValue = hmd5.ComputeHash(dataforMac);
            }
                    
            if ((version[0] == 3) && (version[1] == 2))
            {
                byte[] newArray = new byte[Variables.multipleApplicationData.Length + len];
                Array.Copy(
                        Variables.multipleApplicationData,
                        0,
                        newArray,
                        0,
                        Variables.multipleApplicationData.Length);
                Array.Copy(
                        Variables.decryptedApplicationData,
                        (int)cipherSuite.BlockSize,
                        newArray,
                        Variables.multipleApplicationData.Length,
                        len);
                
                Variables.multipleApplicationData = newArray;
            }
            else
            {
                Utils.AppendArray(
                            ref Variables.multipleApplicationData,
                            Variables.decryptedApplicationData,
                            len);
            }

            if (sourceIP == Variables.clientIP)
            {
                Variables.clientSequenceNumber++;
            }
            else
            {
                Variables.serverSequenceNumber++;
            }

            Variables.Log.LogMessage(actualMacValue, "Actual MAC Value from Decrypted Data");
            Variables.Log.LogMessage(macValue, "Computed MAC Value");

            for (int i = 0; i < macValue.Length; i++)
            {
                if (macValue[i] != actualMacValue[i])
                {
                    Variables.Log.LogMessage("VerifyMacValueTLS.c - MAC Verification failed");
                    return false;
                }
            }

            Variables.Log.LogMessage("MAC Verification Passed");
            return true;
        }
        
        /// <summary>
        /// Method to verify Mac Value for TLS
        /// as per draft-ietf-tls-ssl-version3-00.txt.
        /// </summary>
        /// <param name="cipherSuite">Cipher Suite Information</param>
        /// <param name="sourceIP">Source IP Address</param>
        /// <returns>true if mac value is Ok</returns>
        private static bool VerifyMacvalueSSl(
                                    Variables.CipherSuiteInfo cipherSuite,
                                    string sourceIP)
        {
            SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

            byte pad1 = 0x36;
            byte pad2 = 0x5c;
            byte[] pad1array = new byte[0];
            byte[] pad2array = new byte[0];
            byte[] mackey = new byte[0];
            byte[] contentType = new byte[1];
            int paddingValue = 0;
            byte[] actualMacValue = new byte[0];
            byte[] sequenceNumber = new byte[8];
            byte[] datalength = new byte[2];

            if (cipherSuite.MacType == MAC.SHA1)
            {
                pad1array = new byte[40];
                pad2array = new byte[40];
                mackey = new byte[20];
                actualMacValue = new byte[20];
            }
            else if (cipherSuite.MacType == MAC.MD5)
            {
                pad1array = new byte[48];
                pad2array = new byte[48];
                mackey = new byte[16];
                actualMacValue = new byte[16];
            }

            for (int i = 0; i < pad2array.Length; i++)
            {
                pad1array[i] = pad1;
                pad2array[i] = pad2;
            }

            contentType[0] = Variables.contentType;

            if (sourceIP == Variables.clientIP)
            {
                mackey = Variables.clientMacWriteKey;
                sequenceNumber = BitConverter.GetBytes(
                                    Variables.clientSequenceNumber);
            }
            else
            {
                mackey = Variables.serverMacWriteKey;
                sequenceNumber = BitConverter.GetBytes(
                                    Variables.serverSequenceNumber);
            }

            Array.Reverse(sequenceNumber);

            if (cipherSuite.CipherMode == CipherMode.CBC)
            {
                paddingValue =
                    (int)Variables.decryptedApplicationData[Variables.decryptedApplicationData.Length - 1];

                for (int i = Variables.decryptedApplicationData.Length - paddingValue - 1;
                         i < Variables.decryptedApplicationData.Length; i++)
                {
                    if (Variables.decryptedApplicationData[i] != paddingValue)
                    {
                        Variables.Log.LogMessage("VerifyMacvalueSSl.a - MAC Verification failed");
                        return false;
                    }
                }

                paddingValue++;
            }

            ////Length for the data from Decrypted data for which mac to be computed
            short len = Convert.ToInt16(
                                 Variables.decryptedApplicationData.Length
                                    - actualMacValue.Length
                                    - paddingValue);

            datalength = BitConverter.GetBytes(len);
            Array.Reverse(datalength);

            //// Size of data for mac
            //// decryptedapplication data length
            //// - SHA/MD5 MAC size(16/20) - Padding
            //// + sequence Number(8) + Contenttype(1) +Pad1Value(40/48) + Length(2)
            byte[] dataforInitialMac =
                new byte[Variables.decryptedApplicationData.Length
                         - actualMacValue.Length
                         - paddingValue
                         + pad1array.Length
                         + sequenceNumber.Length
                         + contentType.Length
                         + datalength.Length
                         + mackey.Length];

            Buffer.BlockCopy(
                        mackey,
                        0,
                        dataforInitialMac,
                        0,
                        mackey.Length);
            Buffer.BlockCopy(
                        pad1array,
                        0,
                        dataforInitialMac,
                        mackey.Length,
                        pad1array.Length);
            Buffer.BlockCopy(
                        sequenceNumber,
                        0,
                        dataforInitialMac,
                        mackey.Length + pad1array.Length,
                        sequenceNumber.Length);
            Buffer.BlockCopy(
                        contentType,
                        0,
                        dataforInitialMac,
                        mackey.Length + pad1array.Length + sequenceNumber.Length,
                        contentType.Length);
            Buffer.BlockCopy(
                        datalength,
                        0,
                        dataforInitialMac,
                        mackey.Length + pad1array.Length + sequenceNumber.Length + contentType.Length,
                        datalength.Length);
            Buffer.BlockCopy(
                        Variables.decryptedApplicationData,
                        0,
                        dataforInitialMac,
                        mackey.Length + pad1array.Length + sequenceNumber.Length + contentType.Length + datalength.Length,
                        len);

            byte[] macValue = new byte[0];
            if (cipherSuite.MacType == MAC.SHA1)
            {
                macValue = sha.ComputeHash(dataforInitialMac);
            }
            else if (cipherSuite.MacType == MAC.MD5)
            {
                macValue = md5.ComputeHash(dataforInitialMac);
            }

            byte[] finalDataforMac =
                new byte[macValue.Length + pad2array.Length + mackey.Length];

            Buffer.BlockCopy(
                        mackey,
                        0,
                        finalDataforMac,
                        0,
                        mackey.Length);
            Buffer.BlockCopy(
                        pad2array,
                        0,
                        finalDataforMac,
                        mackey.Length,
                        pad2array.Length);
            Buffer.BlockCopy(
                        macValue,
                        0,
                        finalDataforMac,
                        mackey.Length + pad2array.Length,
                        macValue.Length);

            ////Mac value from decryptedApplicationData
            Buffer.BlockCopy(
                        Variables.decryptedApplicationData,
                        len,
                        actualMacValue,
                        0,
                        actualMacValue.Length);

            macValue = new byte[0];

            if (cipherSuite.MacType == MAC.SHA1)
            {
                macValue = sha.ComputeHash(finalDataforMac);
            }
            else if (cipherSuite.MacType == MAC.MD5)
            {
                macValue = md5.ComputeHash(finalDataforMac);
            }

            Utils.AppendArray(
                        ref Variables.multipleApplicationData,
                        Variables.decryptedApplicationData,
                        len);

            if (sourceIP == Variables.clientIP)
            {
                Variables.clientSequenceNumber++;
            }
            else
            {
                Variables.serverSequenceNumber++;
            }

            for (int i = 0; i < macValue.Length; i++)
            {
                if (macValue[i] != actualMacValue[i])
                {
                    Variables.Log.LogMessage("VerifyMacvalueSSl.b - MAC Verification failed");
                    return false;
                }
            }

            Variables.Log.LogMessage(" MAC Verification Passed");
            return true;
        }

        /// <summary>
        /// Method to verify Mac Value for SSL 0.2
        /// as per http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html.
        /// </summary>
        /// <param name="cipherSuite">Cipher Suite Information</param>
        /// <param name="sourceIP">Source IP Address</param>
        /// <returns>true if mac value is Ok</returns>
        private static bool VerifyMacvalueSSlV2(
                                    Variables.CipherSuiteInfo cipherSuite,
                                    string sourceIP)
        {
            SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

            byte[] secret = new byte[0];
            byte[] actualData = new byte[0];
            byte[] paddingData = new byte[0];
            byte[] sequenceNumber = new byte[4];
            byte[] actualMacValue = new byte[0];

            if (sourceIP == Variables.clientIP)
            {
                secret = new byte[Variables.clientWriteKey.Length];
                Buffer.BlockCopy(Variables.clientWriteKey, 0, secret, 0, Variables.clientWriteKey.Length);
                
                sequenceNumber = BitConverter.GetBytes(
                                   (uint)Variables.clientSequenceNumber);
                Variables.clientSequenceNumber++;
            }
            else
            {
                secret = new byte[Variables.serverWriteKey.Length];
                Buffer.BlockCopy(Variables.serverWriteKey, 0, secret, 0, Variables.serverWriteKey.Length);
                
                sequenceNumber = BitConverter.GetBytes(
                                    (uint)Variables.serverSequenceNumber);
                Variables.serverSequenceNumber++;
            }

            Array.Reverse(sequenceNumber);

            if (cipherSuite.MacType == MAC.MD5)
            {
                actualData = new byte[Variables.decryptedApplicationData.Length - paddingData.Length - 16];
                
                Buffer.BlockCopy(Variables.decryptedApplicationData, 16, actualData, 0, actualData.Length);
                actualMacValue = new byte[16];
            }
            else if (cipherSuite.MacType == MAC.SHA1)
            {
                actualData = new byte[Variables.decryptedApplicationData.Length - paddingData.Length - 20];
                
                Buffer.BlockCopy(Variables.decryptedApplicationData, 20, actualData, 0, actualData.Length);
                actualMacValue = new byte[20];
            }

            //// MAC-DATA = HASH[ SECRET, ACTUAL-DATA, 
            //// PADDING-DATA, SEQUENCE-NUMBER ]
            byte[] macData =
                new byte[secret.Length + actualData.Length + paddingData.Length + sequenceNumber.Length];

            Buffer.BlockCopy(
                        secret,
                        0,
                        macData,
                        0,
                        secret.Length);
            Buffer.BlockCopy(
                        actualData,
                        0,
                        macData,
                        secret.Length,
                        actualData.Length);
            Buffer.BlockCopy(
                        paddingData,
                        0,
                        macData,
                        secret.Length + actualData.Length,
                        paddingData.Length);
            Buffer.BlockCopy(
                        sequenceNumber,
                        0,
                        macData,
                        secret.Length + actualData.Length + paddingData.Length,
                        sequenceNumber.Length);

            byte[] macValue = new byte[0];

            if (cipherSuite.MacType == MAC.SHA1)
            {
                macValue = sha.ComputeHash(macData);
            }
            else if (cipherSuite.MacType == MAC.MD5)
            {
                macValue = md5.ComputeHash(macData);
            }
            
            Utils.AppendArray(
                        ref Variables.multipleApplicationData,
                        actualData,
                        actualData.Length - Variables.sslv2Padding);

            Variables.sslv2Padding = 0;
            ////Mac value from decryptedApplicationData
            Buffer.BlockCopy(
                        Variables.decryptedApplicationData,
                        0,
                        actualMacValue,
                        0,
                        actualMacValue.Length);

            for (int i = 0; i < macValue.Length; i++)
            {
                if (macValue[i] != actualMacValue[i])
                {
                    Variables.Log.LogMessage("VerifyMacvalueSSlV2 - MAC Verification failed");
                    return false;
                }
            }

            Variables.Log.LogMessage(" MAC Verification Passed");
            return true;
        }
        
        #endregion
    }
}